﻿/******************************************************************************
 * Copyright (c) 2011, S. Kyle Korndoerfer
 * All rights reserved.
 * 
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 * 
 *  * Redistributions of source code must retain the above copyright notice, 
 *      this list of conditions and the following disclaimer.
 *  * Redistributions in binary form must reproduce the above copyright
 *      notice, this list of conditions and the following disclaimer in the 
 *      documentation and/or other materials provided with the distribution.
 *  * Neither the name of KyKo Software nor the names of its contributors may 
 *      be used to endorse or promote products derived from this software 
 *      without specific prior written permission.
 * 
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 
 * POSSIBILITY OF SUCH DAMAGE. 
 *****************************************************************************/

using System;
using System.Diagnostics;
using System.Drawing;
using System.IO;
using System.Windows.Forms;

using wsdEditor.Data;
using wsdEditor.Localization;
using wsdEditor.UI.WinForms.Properties;

namespace wsdEditor.UI.WinForms
{
    /// <summary>
    ///     Main application form.
    /// </summary>
    public partial class MainForm : Form
    {
        #region Fields

        /// <summary>
        ///     Indicates if the contents of the editor have been modified.
        /// </summary>
        private bool saveNeeded;

        #endregion
        
        #region Constructor(s)

        /// <summary>
        ///     Initializes a new instance of the form.
        /// </summary>
        public MainForm()
        {
            InitializeComponent();

            Icon = Resources.AppIcon;

            InitializeNewProject();
            InitializeDisplayStyles();
        }// MainForm()

        #endregion
        
        #region events

        /// <summary>
        ///     Prompt the user if they want to save their changes (if
        ///     needed) before the form is closed.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void MainForm_FormClosing( object sender, FormClosingEventArgs e )
        {
            if ( saveNeeded )
            {
                var result = MessageBox.Show(
                        "You have unsaved changes. Do you wish to save before proceeding?",
                        "Save Changes?",
                        MessageBoxButtons.YesNoCancel,
                        MessageBoxIcon.Question );

                switch ( result )
                {
                    // Yes = save & close
                    case DialogResult.Yes:
                        SaveProject();

                        if ( !SaveNeeded )
                        {
                            e.Cancel = true;
                        }// if

                        break;
                    // No = don't save & close
                    case DialogResult.No:
                        break;
                    // Cancel = do nothing
                    case DialogResult.Cancel:
                        e.Cancel = true;
                        break;
                }// switch
            }// if
        }// MainForm_FormClosing( object sender, FormClosingEventArgs e )

        /// <summary>
        ///     Calls out to the web service to generate the diagram based on the sequence text.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void RefreshPreviewButton_Click( object sender, EventArgs e )
        {
            UpdatePreviewImage();
        }// GenerateButton_Click( sender, e )

        /// <summary>
        ///     Handles the selecting event for the tab control. If the tab being
        ///     selected is the Preview tab, refresh the sequence image (if 
        ///     changes have been made).
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void TabControl_Selecting( object sender, TabControlCancelEventArgs e )
        {
            if ( e.TabPage == mPreviewTab )
            {
                UpdatePreviewImage();
            }
        }// TabControl_Selecting( sender, e )

        /// <summary>
        ///     Sets the SaveNeeded flag in response to the text in the
        ///     sequence editor being changed.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void SequenceTextBox_TextChanged( object sender, EventArgs e )
        {
            SaveNeeded = true;
        }// SequenceTextBox_TextChanged( sender, e )

        #endregion

        #region Helper methods

        /// <summary>
        ///     Builds a <see cref="wsdProject" /> instance for the current
        ///     project.
        /// </summary>
        /// <returns>A <see cref="wsdProject" /> instance.</returns>
        private wsdProject BuildProject()
        {
            var seq = new wsdProjectSequencesSequence
                          {
                              data = mSequenceTextBox.Text,
                              description = "(SEQUENCE DESCRIPTION)",
                              name = "(SEQUENCE NAME)",
                              options = new wsdProjectSequencesSequenceOptions
                                            {
                                                scale = mScaleTrackBar.Value.ToString(),
                                                style = mStyleComboBox.SelectedText ?? "napkin"
                                            }
                          };

            var project = new wsdProject
                              {
                                  description = "(DESCRIPTION)",
                                  version = 1.0m,
                                  sequences = new wsdProjectSequences
                                                  {
                                                      sequence = seq
                                                  }
                              };

            return project;
        }// BuildProject()

        /// <summary>
        ///     Shows the Save dialog to allow the user to specifiy where they
        ///     want to save the project.
        /// </summary>
        private void ShowSaveDialog()
        {
            if ( !string.IsNullOrEmpty( Filename ) )
            {
                return;
            }// if

            var result = mSaveFileDialog.ShowDialog( this );

            if( result == DialogResult.OK )
            {
                Filename = mSaveFileDialog.FileName;
            }// if
        }// ShowSaveDialog()

        /// <summary>
        ///     Attempts to save the current project.
        /// </summary>
        private void SaveProject()
        {
            ShowSaveDialog();

            if ( !string.IsNullOrEmpty( Filename ) )
            {
                try
                {
                    // build project instance
                    var project = BuildProject();

                    ProjectFileManager.SaveFile(project, Filename);

                    SaveNeeded = false;
                }
                catch ( IOException ex )
                {
                    MessageBox.Show(
                            string.Format( 
                                    "Unable to save the project.{0}{0}Reason: {1}", 
                                    Environment.NewLine, 
                                    ex.Message ),
                            "Error saving project", 
                            MessageBoxButtons.OK, 
                            MessageBoxIcon.Error );
                } // try/catch
            }// if
        }// SaveProject()

        /// <summary>
        ///     Prompts the user for a file to load using the Open File dialog.
        /// </summary>
        private void LoadProject()
        {
            // is a save needed?
            if ( SaveNeeded )
            {
                var saveResult = MessageBox.Show(
                        "Do you wish to save your changes?",
                        "Save Changes?",
                        MessageBoxButtons.YesNo,
                        MessageBoxIcon.Question );

                if ( saveResult == DialogResult.Yes )
                {
                    SaveProject();
                }
                else
                {
                    SaveNeeded = false;
                }// if/else
            }// if

            // If project was saved (or not needed), proceed. Otherwise do nothing.
            if ( !SaveNeeded )
            {
                var result = mOpenFileDialog.ShowDialog();

                if ( result == DialogResult.OK )
                {
                    Filename = mOpenFileDialog.FileName;

                    var project = ProjectFileManager.LoadFile( Filename );

                    // update the form
                    // TODO: need to refactor better!
                    mSequenceTree.BeginUpdate();
                    mSequenceTree.UseWaitCursor = true;
                    mSequenceTree.Nodes.Clear();

                    mSequenceTree.Nodes.Add( new TreeNode( project.name, 0, 0 ) );
                    mSequenceTree.Nodes[ 0 ].Nodes.Add( new TreeNode( project.sequences.sequence.name, 1, 1 ) );

                    mSequenceTree.SelectedNode = mSequenceTree.Nodes[ 0 ];
                    mSequenceTree.UseWaitCursor = false;
                    mSequenceTree.EndUpdate();

                    mSequenceTextBox.Text = project.sequences.sequence.data;
                    mSequenceTextBox.SelectionStart = 0;
                    mSequenceTextBox.SelectionLength = 0;

                    mScaleTrackBar.Value = Int32.Parse( project.sequences.sequence.options.scale );
                    mStyleComboBox.SelectedValue = project.sequences.sequence.options.style;

                    SaveNeeded = false;
                }// if
            }// if
        }// LoadProject()

        /// <summary>
        ///     Initializes a new (blank) project on application start when
        ///     not specifying a file to load or in response to the 
        ///     <i>File | New | Project</i> menu item.
        /// </summary>
        private void InitializeNewProject()
        {
            // Initialize the Sequence Tree
            mSequenceTree.BeginUpdate();
            mSequenceTree.UseWaitCursor = true;
            mSequenceTree.Nodes.Clear();

            mSequenceTree.Nodes.Add( new TreeNode( "New Project", 0, 0 ) );
            mSequenceTree.Nodes[ 0 ].Nodes.Add( new TreeNode( "Sequence 1", 1, 1 ) );

            mSequenceTree.ExpandAll();
            mSequenceTree.SelectedNode = mSequenceTree.Nodes[ 0 ];
            mSequenceTree.UseWaitCursor = false;
            mSequenceTree.EndUpdate();

            // initialize the Description
            mDescriptionTextBox.Text = Strings.Translate( "NewProject_Name" );

            // initialize the default text in the editor
            mSequenceTextBox.Text = Settings.Default.NewSequenceText;

            SaveNeeded = false;
        }// InitializeNewProject()

        /// <summary>
        ///     Initializes the values contained in the Display Styles combo box.
        /// </summary>
        private void InitializeDisplayStyles()
        {
            mStyleComboBox.Items.Clear();
            mStyleComboBox.Items.AddRange( WebSequenceDiagrams.Styles.GetList().ToArray() );
        }// InitializeDisplayStyles()

        /// <summary>
        ///     Fetches an updated sequence diagram from the service.
        /// </summary>
        private void UpdatePreviewImage()
        {
            var service = new WebSequenceDiagrams.Service();

            service.DownloadComplete += ServiceDownloadCompleted;
            service.GetSequenceDiagram(
                    (string)mStyleComboBox.SelectedItem,
                    mScaleTrackBar.Value.ToString(),
                    mSequenceTextBox.Text,
                    null );
        }// UpdatePreviewImage()

        /// <summary>
        ///     Handles the response from the service.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        void ServiceDownloadCompleted( object sender, WebSequenceDiagrams.DownloadCompleteEventArgs e )
        {
            if ( e.Data != null && e.Error == null )
            {
                mPreviewPictureBox.Image = Image.FromStream( new MemoryStream( e.Data ) );
                mTabControl.SelectedTab = mPreviewTab;
            }
            else
            {
                MessageBox.Show(
                        e.Error.Message + Environment.NewLine + e.Error.StackTrace,
                        "Error downloading data",
                        MessageBoxButtons.OK,
                        MessageBoxIcon.Error );
            }// if
        }// DownloadDataCompleted( sender, e )

        #endregion

        #region Properties

        /// <summary>
        ///     Indicates whether the sequence needs to be saved.
        /// </summary>
        private bool SaveNeeded
        {
            [DebuggerStepThrough]
            get { return saveNeeded; }
            
            set
            {
                saveNeeded = value;

                if ( saveNeeded && !mEditorTab.Text.EndsWith( "*" ) )
                {
                    mEditorTab.Text += " *";
                    mSaveMenuItem.Enabled = true;
                }// if
                
                if ( !saveNeeded && mEditorTab.Text.EndsWith(  "*" ) )
                {
                    mEditorTab.Text = mEditorTab.Text.Substring( 0, mEditorTab.Text.Length - 2 );
                    mSaveMenuItem.Enabled = false;
                }// if
            }// set
        }// SaveNeeded

        /// <summary>
        ///     The full path and name of the file to save the project to.
        /// </summary>
        private string Filename
        {
            [DebuggerStepThrough]
            get;

            [DebuggerStepThrough]
            set;
        }// Filename

        #endregion
    }

}